package com.soulmate.auth;

import com.soulmate.auth.authencation.TelephoneCodeAuthenticationProvider;
import com.soulmate.auth.authencation.UserAuthenticationProvider;
import com.soulmate.auth.authencation.WeiXinCodeAuthenticationProvider;
import com.soulmate.auth.authencation.filter.TelephoneCodeAuthenticationFilter;
import com.soulmate.auth.filter.AuthTokenFilter;
import com.soulmate.auth.handle.*;
import com.soulmate.auth.permission.UserPermissionEvaluator;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;


/**
 * 网络安全配置
 *
 * @author huaiping
 */
@Configuration
@EnableWebSecurity
@AllArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {

    private LoginFailureHandler loginFailureHandler;
    private LoginSuccessHandler loginSuccessHandler;
    private UserAuthAccessDeniedHandler userAuthAccessDeniedHandler;
    private UserAuthenticationEntryPointHandler userAuthenticationEntryPointHandler;
    private LogoutSuccessHandler logoutSuccessHandler;
    private UserPermissionEvaluator userPermissionEvaluator;
    private UserAuthenticationProvider  userAuthenticationProvider;
    private TelephoneCodeAuthenticationProvider telephoneAuthenticationProvider;
    private WeiXinCodeAuthenticationProvider weiXinCodeAuthenticationProvider;


    /**
     * 指定加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 使用BCrypt加密密码
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthTokenFilter authenticationAuthTokenFilter() {
        return new AuthTokenFilter();
    }

    @Bean
    public TelephoneCodeAuthenticationFilter telephoneCodeAuthenticationFilter() {
        TelephoneCodeAuthenticationFilter telephoneCodeAuthenticationFilter =
                new TelephoneCodeAuthenticationFilter();
        telephoneCodeAuthenticationFilter.setAuthenticationSuccessHandler(loginSuccessHandler);
        telephoneCodeAuthenticationFilter.setAuthenticationFailureHandler(loginFailureHandler);
        return telephoneCodeAuthenticationFilter;
    }

    @Bean
    public AuthenticationManager authenticationManager() {
       return new ProviderManager(userAuthenticationProvider, telephoneAuthenticationProvider,
               weiXinCodeAuthenticationProvider);
    }

    /**
     * 注入自定义PermissionEvaluator
     */
    @Bean
    public DefaultWebSecurityExpressionHandler userSecurityExpressionHandler() {
        DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
        handler.setPermissionEvaluator(userPermissionEvaluator);
        return handler;
    }

    /**
     * 安全过滤器链
     *
     * @param httpSecurity http安全性
     * @return {@link SecurityFilterChain}
     * @throws Exception 异常
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .authorizeHttpRequests(requestMatcherRegistry ->
                        requestMatcherRegistry.antMatchers("/user/login/*").permitAll()
                                .anyRequest().authenticated()
                )
                .formLogin(formLoginConfigurer ->
                        formLoginConfigurer.loginProcessingUrl("/user/login/from")
                                .successHandler(loginSuccessHandler)
                                .failureHandler(loginFailureHandler))
                .logout(logoutConfigurer ->
                        logoutConfigurer.logoutUrl("/user/logout")
                                .logoutSuccessHandler(logoutSuccessHandler))
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
                        httpSecurityExceptionHandlingConfigurer
                                .authenticationEntryPoint(userAuthenticationEntryPointHandler)
                                .accessDeniedHandler(userAuthAccessDeniedHandler)
                )
                .httpBasic().disable()
                .csrf().disable()
                .authenticationProvider(userAuthenticationProvider)
                .authenticationProvider(telephoneAuthenticationProvider)
                .authenticationProvider(weiXinCodeAuthenticationProvider)
                .addFilterBefore(telephoneCodeAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(authenticationAuthTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        return httpSecurity.build();
    }


}
